--- /dev/null
+'use strict';
+'require view';
+'require form';
+
+function validateAtsign(section_id, value) {
+ if (value.length < 1) {
+ return _('Must not be empty and should start with @ (e.g., "@a").');
+ }
+ return true;
+}
+
+function validateDevice(section_id, value) {
+ if (value.length < 1) {
+ return _('Must be at least one character long (e.g., "a").');
+ }
+ if (value.length == 1) {
+ if (!/^[a-z]+$/.test(value)) {
+ return _('First character should be a lowercase letter (e.g., "a").');
+ } else {
+ return true;
+ }
+ }
+ if (!/^[a-z][a-z0-9_-]+$/.test(value)) {
+ return _('Device names may contain a-z 0-9 _ or - (e.g., "my_thing1").');
+ }
+ if (value.length > 36) {
+ return _('Maximum device name length is 36 characters.');
+ }
+ return true;
+}
+
+function validateOTP(section_id, value) {
+ if (value.length != 6) {
+ return _('Must be six characters (e.g., "S3CR3T").');
+ }
+ return true;
+}
+
+function firstAt(section_id, value) {
+ if (value && !value.startsWith('@')) {
+ value = '@' + value; // Ensure @ at start
+ }
+ return this.super('write', [section_id, value]);
+}
+
+return view.extend({
+ render: function() {
+ let m, s, o;
+
+ m = new form.Map('sshnpd', _('NoPorts'),
+ _('Daemon Configuration'));
+
+ s = m.section(form.TypedSection, 'sshnpd', _('sshnpd config'));
+ s.anonymous = true;
+
+ o = s.option(form.Value, 'atsign', _('Device atSign'),
+ _('The device atSign e.g. @device'));
+ o.default = '@device';
+ o.validate = validateAtsign;
+ o.write = firstAt;
+
+ o = s.option(form.Value, 'manager', _('Manager atSign'),
+ _('The manager atSign e.g. @manager'));
+ o.default = '@manager';
+ o.validate = validateAtsign;
+ o.write = firstAt;
+
+ o = s.option(form.Value, 'device', _('Device name'),
+ _('The name for this device e.g. openwrt'));
+ o.default = 'openwrt';
+ o.validate = validateDevice;
+
+ s.option(form.Value, 'args', _('Additional arguments'),
+ _('Further command line arguments for the NoPorts daemon'));
+
+ o = s.option(form.Value, 'otp', _('Enrollment OTP/SPP'),
+ _('One Time Passcode (OTP) for device atSign enrollment'));
+ o.default = '000000';
+ o.validate = validateOTP;
+
+ o = s.option(form.Flag, 'enabled', _('Enabled'),
+ _('Check here to enable the service'));
+ o.default = '1';
+ o.rmempty = false;
+
+ return m.render();
+ },
+});
--- /dev/null
+'use strict';
+'require view';
+'require dom';
+'require fs';
+'require ui';
+'require uci';
+'require network';
+
+
+return view.extend({
+ handleCommand: function(exec, args) {
+ let buttons = document.querySelectorAll('.diag-action > .cbi-button');
+
+ for (let i = 0; i < buttons.length; i++)
+ buttons[i].setAttribute('disabled', 'true');
+
+ return fs.exec(exec, args).then(function(res) {
+ let out = document.querySelector('textarea');
+
+ dom.content(out, [ res.stdout || '', res.stderr || '' ]);
+ }).catch(function(err) {
+ ui.addNotification(null, E('p', [ err ]))
+ }).finally(function() {
+ for (let i = 0; i < buttons.length; i++)
+ buttons[i].removeAttribute('disabled');
+ });
+ },
+
+ handleEnroll: function() {
+ return this.handleCommand('at_enroll.sh', "");
+ },
+
+ load: function() {
+ return uci.load('sshnpd').then(function() {
+ let atsign = uci.get_first('sshnpd','','atsign'),
+ keyfile = '/root/.atsign/keys/'+atsign+'_key.atKeys';
+ return L.resolveDefault(fs.stat(keyfile), {});
+ });
+ },
+
+ render: function(res) {
+
+ const has_atkey = res.path;
+ const atsign = uci.get_first('sshnpd','','atsign');
+ const device = uci.get_first('sshnpd','','device');
+ const otp = uci.get_first('sshnpd','','otp');
+ const enrollready = atsign && device && otp && !has_atkey;
+
+ const instructions = E('div', { 'class': 'cbi-map-descr'}, _('Press the Enroll button then run this command on a system where '+atsign+' is activated:'));
+
+ const enrollcmd = E('code','at_activate approve -a '+atsign+' --arx noports --drx '+device);
+
+ let table = E('table', { 'class': 'table' }, [
+ E('tr', { 'class': 'tr' }, [
+ E('td', { 'class': 'td left' }, [
+ E('span', { 'class': 'diag-action' }, [
+ E('button', {
+ 'class': 'cbi-button cbi-button-action',
+ 'click': ui.createHandlerFn(this, 'handleEnroll')
+ }, [ _('Enroll') ])
+ ])
+ ]),
+ ])
+ ]);
+
+ const cmdwindow = E('div', {'class': 'cbi-section'}, [
+ E('div', { 'id' : 'command-output'},
+ E('textarea', {
+ 'id': 'widget.command-output',
+ 'style': 'width: 100%; font-family:monospace; white-space:pre',
+ 'readonly': true,
+ 'wrap': 'on',
+ 'rows': '20'
+ })
+ )
+ ]);
+
+ let view = E('div', { 'class': 'cbi-map'}, [
+ E('h2', {}, [ _('NoPorts atSign Enrollment') ]),
+ atsign ? E([]) : E('div', { 'class': 'cbi-map-descr'}, _('atSign must be configured')),
+ device ? E([]) : E('div', { 'class': 'cbi-map-descr'}, _('Device must be configured')),
+ otp ? E([]) : E('div', { 'class': 'cbi-map-descr'}, _('OTP must be configured. An OTP can be generated using:')),
+ otp ? E([]) : E('code','at_activate otp -a '+atsign),
+ has_atkey ? E('div', { 'class': 'cbi-map-descr'}, _('Existing key found at: '+has_atkey)) : E([]),
+ enrollready ? instructions : E([]),
+ enrollready ? enrollcmd : E([]),
+ enrollready ? table : E([]),
+ enrollready ? cmdwindow : E([]),
+ ]);
+
+ return view;
+ },
+
+ handleSaveApply: null,
+ handleSave: null,
+ handleReset: null
+});